/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hardware;

import aplicacao.TelaPrincipal;
import aplicacao.TelaSimulador;
import auxiliar.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;
import politicasdesubstituicao.FIFO;
import politicasdesubstituicao.LRU;

/**
 *
 * @author Schettino
 */
public abstract class MemoriaPrincipal {

    private static int TAMANHO_PROCESSOS = 0;
    private static int TAMANHO_TP = 0;
    private static int espacoDisponivel = 0;
    private static int espacoDisponivelTP = 0;
    private static Quadro[] quadros;
    private static List<Pagina> filaDePaginas = new ArrayList<Pagina>();
    private static List<TabelaDePaginas> tabelas = new ArrayList<TabelaDePaginas>();

    public static void adicionarTP(Processo processo) {
        TabelaDePaginas tabelaDePaginas = processo.getTabelaDePaginas();
        TelaSimulador.adicionarTP(processo);
        int tamanho = tabelaDePaginas.getLinhas().size();
        if (MemoriaPrincipal.espacoDisponivelTP < tamanho) {
            JOptionPane.showMessageDialog(null, "Espaço em memória insuficiente", "Erro", JOptionPane.ERROR_MESSAGE);
        } else {
            MemoriaPrincipal.espacoDisponivelTP -= tamanho;
            MemoriaPrincipal.tabelas.add(tabelaDePaginas);
        }
    }

    public static int proximoQuadroDisponivel() {
        for (int i = 0; i < quadros.length; i++) {
            if (quadros[i].getPagina() == null) {
                return i;
            }
        }
        return -1;
    }

    public static void adicionarPagina(Pagina pagina, Quadro quadro) {
        Pagina paginaOut = quadro.getPagina();
        Processo processoOut = paginaOut.getProcesso();
        int iOut = processoOut.getPaginas().indexOf(paginaOut);
        LinhaTP linhaTPOut = processoOut.getTabelaDePaginas().getLinhas().get(iOut);
        linhaTPOut.setP(false);
        linhaTPOut.setM(false);
        linhaTPOut.setNumQuadro(-1);
        if (processoOut.getTabelaDePaginas().getLinhas().get(iOut).isM()) {
            MemoriaSecundaria.atualizarPagina(paginaOut);
        }
        JOptionPane.showMessageDialog(null, "Página " + iOut + " do Processo " + processoOut.getId() + " (Quadro " + quadro.getEnderecoFisico() + " ) sofrerá swap-out", "Aviso de Swap", JOptionPane.INFORMATION_MESSAGE);
        TelaSimulador.atualizaTabelaDePaginas(paginaOut);
        pagina.setUltimaReferencia(TelaSimulador.relogio);
        pagina.setEntradaMP(TelaSimulador.relogio);
        quadro.setPagina(pagina);
        Processo processo = pagina.getProcesso();
        int i = processo.getPaginas().indexOf(pagina);
        LinhaTP linha = processo.getTabelaDePaginas().getLinhas().get(i);
        linha.setNumQuadro(quadro.getEnderecoFisico());
        linha.setP(true);
        TelaSimulador.atualizaTabelaMP(pagina, quadro);
        TelaSimulador.incrementaFaltaDePaginas();
    }

    public static void adicionarPagina(Pagina pagina) {
        int numQuadro = proximoQuadroDisponivel();
        if (numQuadro == -1) {
            JOptionPane.showMessageDialog(null, "Espaço em memória principal esgotado", "Erro", JOptionPane.ERROR_MESSAGE);
            return;
        }
        pagina.setUltimaReferencia(TelaSimulador.relogio);
        pagina.setEntradaMP(TelaSimulador.relogio);
        Quadro quadro = MemoriaPrincipal.getQuadros()[numQuadro];
        quadro.setPagina(pagina);
        Processo processo = pagina.getProcesso();
        int i = processo.getPaginas().indexOf(pagina);
        LinhaTP linha = processo.getTabelaDePaginas().getLinhas().get(i);
        linha.setNumQuadro(numQuadro);
        linha.setP(true);
        linha.setM(false);
        TelaSimulador.atualizaTabelaMP(pagina, quadro);
        MemoriaPrincipal.setEspacoDisponivel(MemoriaPrincipal.getEspacoDisponivel() - 1);
    }

    public static Quadro[] getQuadros() {
        return quadros;
    }

    public static List<Pagina> getFilaDePaginas() {
        return filaDePaginas;
    }

    public static int getTAMANHO() {
        return TAMANHO_PROCESSOS;
    }

    public static void setTAMANHO(int TAMANHO) {
        MemoriaPrincipal.TAMANHO_PROCESSOS = TAMANHO;
        MemoriaPrincipal.quadros = new Quadro[TAMANHO];
    }

    public static void setQuadros(Quadro[] celulas) {
        MemoriaPrincipal.quadros = celulas;
    }

    public static void setFilaDePaginas(List<Pagina> fila) {
        MemoriaPrincipal.filaDePaginas = fila;
    }

    public static boolean lerInstrucao(int idProcesso, int enderecoVirtual) {
        Processo processo = TelaSimulador.getProcessos().get(idProcesso - 1);
        int numPagina = enderecoVirtual / Pagina.getTAMANHO();
        LinhaTP linha = processo.getTabelaDePaginas().getLinhas().get(numPagina);
        Pagina pagina = processo.getPaginas().get(numPagina);

        switch (TelaPrincipal.politica) {
            case (TelaPrincipal.POLITICA_FIFO):
                if (linha.isP()) {
                    Quadro quadro = MemoriaPrincipal.getQuadros()[linha.getNumQuadro()];
                    FIFO.organizarFila(quadro.getPagina());
                    pagina.setUltimaReferencia(TelaSimulador.relogio);
                    TelaSimulador.atualizaTabelaMP(pagina, quadro);
                } else {
                    return FIFO.alocarPagina(processo.getPaginas().get(numPagina));
                }
                break;
            case (TelaPrincipal.POLITICA_LRU):
                if (linha.isP()) {
                    Quadro quadro = MemoriaPrincipal.getQuadros()[linha.getNumQuadro()];
                    LRU.organizarFila(quadro.getPagina());
                    pagina.setUltimaReferencia(TelaSimulador.relogio);
                    TelaSimulador.atualizaTabelaMP(pagina, quadro);
                } else {
                    return LRU.alocarPagina(pagina);
                }
                break;
        }
        return true;
    }

    public static boolean escreverInstrucao(int idProcesso, int enderecoVirtual) {
        if (lerInstrucao(idProcesso, enderecoVirtual)) {
            Processo processo = TelaSimulador.getProcessos().get(idProcesso - 1);
            int numPagina = enderecoVirtual / Pagina.getTAMANHO();
            LinhaTP linha = processo.getTabelaDePaginas().getLinhas().get(numPagina);
            linha.setM(true);
            linha.setP(true);
            Pagina pagina = processo.getPaginas().get(numPagina);
            pagina.incrementarVersao();
            Quadro quadro = MemoriaPrincipal.getQuadros()[linha.getNumQuadro()];
            TelaSimulador.atualizaTabelaDePaginas(pagina);
            TelaSimulador.atualizaTabelaMP(pagina, quadro);
            return true;
        } else {
            return false;
        }
    }

    public static void setEspacoDisponivel(int espacoDisponivel) {
        MemoriaPrincipal.espacoDisponivel = espacoDisponivel;
    }

    public static int getEspacoDisponivel() {
        return espacoDisponivel;
    }

    public static int getEspacoDisponivelTP() {
        return espacoDisponivelTP;
    }

    public static void setEspacoDisponivelTP(int espacoDisponivelTP) {
        MemoriaPrincipal.espacoDisponivelTP = espacoDisponivelTP;
    }

    public static List<TabelaDePaginas> getTabelas() {
        return tabelas;
    }

    public static void setTabelas(List<TabelaDePaginas> tabelas) {
        MemoriaPrincipal.tabelas = tabelas;
    }

    public static int getTAMANHO_TP() {
        return TAMANHO_TP;
    }

    public static void setTAMANHO_TP(int TAMANHO_TP) {
        MemoriaPrincipal.TAMANHO_TP = TAMANHO_TP;
    }
}
